'''
# 作者    ： 张莹潇
# 创建时间 ： 20/12/12 14:52
'''
# encoding: UTF-8
import numpy as np
import pandas as pd
import scipy.stats as scs
import csv
from datetime import datetime
from dateutil.relativedelta import relativedelta
from time import sleep
from pyecharts.charts import (Bar,Line,Graph,Gauge,Page)#柱状图，折线图，关系图，仪表盘,多图同表
from pyecharts import options as opts
from empyrical import (sortino_ratio,omega_ratio,annual_volatility,cagr,conditional_value_at_risk,downside_risk,stability_of_timeseries,tail_ratio,value_at_risk,calmar_ratio)
#年总交易日
TRADING_DAY = 252
def output(msg):
    print(f'{datetime.now()}\t{msg}')
#----------------------------------------------------------------------
def statistics_status(array):
    """返回array均值，标准差，偏度，峰度"""
    stats = scs.describe(array)
    return stats[2],np.sqrt(stats[3]),stats[4],stats[5]
#------------------------------------------------------------------
def plot_account(total_date:int = 0):
    ctp_account_path = "ctp_account.csv"    #ctp账户csv数据路径
    df = pd.DataFrame()
    account_day = []     #交易日
    account_balance = []     #总资金
    account_commission = []  #手续费
    net_value_list = []   #净值
    net_value = 0
    #月度盈亏参数
    last_month_date = ""
    month_pnl = 0
    month_dict = {}
    #------------------------------------------------------------------
    with open(ctp_account_path,"r",encoding="utf-8") as f1:
        csvreader = csv.DictReader((line.replace("\0",'') for line in f1),delimiter=",") #line.replace移除NULL值
        ctp_account = [rowdata for rowdata in csvreader]
    for index in range(len(ctp_account)):
        account_day.append(ctp_account[index]["datetime"])  #交易日
        account_balance.append(float(ctp_account[index]["balance"]))
        account_commission.append(float(ctp_account[index]["commission"]))
    if total_date:
        df["balance"] = account_balance[-total_date:]
        df["commission"] = account_commission[-total_date:]
        df["datetime"] = account_day[-total_date:]
    else:
        df["balance"] = account_balance
        df["commission"] = account_commission
        df["datetime"] = account_day
    for index in range(len(df["balance"])):
        if index == 0:
            net_pnl = 1
        else:
            net_pnl = df["balance"][index]/df["balance"][index-1]-1
        net_value += net_pnl
        net_value_list.append((float(net_value)))
    df["highlevel"] = df["balance"].rolling(min_periods=1,window=len(df),center=False).max()    #净值高点
    df["drawdown"] = df["balance"] - df["highlevel"]    #回撤资金
    df["drawdown_percent"] = (df["balance"] - df["highlevel"])/df["highlevel"]         #回撤百分比
    total_return = (df["balance"].values[-1]/df["balance"].values[0]-1)*100  #总收益率
    total_days = len(df) / 2        #数字货币接口会重复记录一天的账户数据,实际交易日减半
    annual_return = total_return / total_days * TRADING_DAY         #年化收益率
    max_drawdown = df["drawdown"].min()      #最大回撤资金
    mad_dd_percent = df["drawdown_percent"].min()*100        #最大回撤率

    df["return"] = (np.log(df["balance"]) - np.log(df["balance"].shift(1))).fillna(0)   #净收益率
    df['net_pnl'] = df["balance"] - df["balance"].shift(1)  #日盈亏
    df['net_pnl'].fillna(0,inplace = True)
    df.set_index("datetime",inplace=True)

    #收益率均值，标准差，偏度，峰度
    return_mean,return_std,return_skew, return_kurt = statistics_status(df["return"].values)
    if return_std:
        sharpe_ratio = df["return"].mean() * 100 / (return_std * 100) * np.sqrt(252)
    else:
        sharpe_ratio = 0
    #sortino_info
    sortino_info = sortino_ratio(df['return'])
    omega_info = omega_ratio(df['return'])
    #calmar_info
    calmar_info = calmar_ratio(df['return'],period="yearly")
    #年化波动率
    annual_volatility_info = annual_volatility(df['return'])
    #年化复合增长率
    cagr_info = cagr(df['return'])
    annual_downside_risk = downside_risk(df['return'],required_return = 0.2,period = 'yearly')
    c_var = conditional_value_at_risk(df['return'])
    var_info = value_at_risk(df['return'])
    stability_return = stability_of_timeseries(df['return'])
    #尾部比率0.25 == 1/4,收益1，风险4
    tail_ratio_info = tail_ratio(df['return'])
    #--------------------------------------------------------------------------------------------
    output("-"*70)
    output(f"账户:{ctp_account[-1]['accountid']}，总资金：{float(ctp_account[-1]['balance']):,.3f}，可用资金剩余:{float(ctp_account[-1]['available']):,.3f}，rmb总资金：{float(ctp_account[-1]['pre_balance']):,.3f}")
    output(f"总盈亏:{df['balance'].values[-1]-df['balance'].values[0]:,.3f}，总收益率:{total_return:,.3f}%，复利净值：{net_value_list[-1]:,.3f}")
    output(f"sharpe_ratio：\t{sharpe_ratio:,.3f}")
    output(f"calmar_info：\t{calmar_info:,.3f}")
    output(f"sortino_info：\t{sortino_info:,.3f}")
    output(f"omega_info：\t{omega_info:,.3f}")
    output(f"年化波动率：\t{annual_volatility_info:,.3f}")
    output(f"年复合增长率：\t{cagr_info:,.3f}")
    output(f"年化下行风险率：\t{annual_downside_risk:,.3f}")
    output(f"c_var：\t{c_var:,.3f}")
    output(f"var_info：\t{var_info:,.3f}")
    output(f"收益稳定率：\t{stability_return:,.3f}")
    output(f"尾部比率：\t{tail_ratio_info:,.3f}")
    output(f"持仓盈亏:{float(ctp_account[-1]['position_profit']):,.3f}")
    output(f"最大回撤率：{mad_dd_percent:,.3f}%")
    output(f"收益回撤比：{-np.sum(df['net_pnl'])/max_drawdown:,.3f}")
    output(f"收益率回撤率比：{-total_return/mad_dd_percent:,.3f}")
    output(f"总手续费：{df['commission'].sum():,.3f}")
    output(f"年化收益率：\t{annual_return:,.3f}%")
    output(f"日均盈亏：{np.mean(df['net_pnl']):,.3f}\t日盈利最大值：{max(df['net_pnl']):,.3f}\t日亏损最大值:{min(df['net_pnl']):,.3f}")
    output(f"日均收益率：{return_mean*100:,.3f}%，收益率标准差：{return_std*100:,.3f}%，收益率偏度：{return_skew:,.3f}，收益率峰度：{return_kurt:,.3f}")
    output("-"*70)
    #--------------------------------------------------------------------------------------------
    page = Page()
    bar_1 = Line()
    bar_1.add_xaxis(df['balance'].index.tolist())
    bar_1.add_yaxis(f"账户:{ctp_account[-1]['accountid']}\n\n总资金\n\n起止时间：{df['balance'].index[0]}至{df['balance'].index[-1]}",df['balance'].tolist())      #主标题
    bar_1.set_global_opts(opts.TitleOpts(title=f"资金\n\n总收益率：{total_return:,.3f}%"),toolbox_opts=opts.ToolboxOpts())  #副标题，ToolboxOpts设置工具箱配置项
    bar_1.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项

    bar_2 = Bar()
    bar_2.add_xaxis(df.index.tolist())
    bar_2.add_yaxis(f"复利净值最高点：{max(net_value_list):,.3f}\t复利净值最低点：{min(net_value_list):,.3f}",net_value_list)      #主标题
    bar_2.set_global_opts(opts.TitleOpts(title="复利净值"),toolbox_opts=opts.ToolboxOpts())  #副标题，ToolboxOpts设置工具箱配置项
    bar_2.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项

    bar_3 = Bar()
    bar_3.add_xaxis(df.index[1:].tolist())
    bar_3.add_yaxis(f"日盈利最大值：{max(df['net_pnl']):,.3f}\n\n日亏损最大值:{min(df['net_pnl']):,.3f}",df['net_pnl'].tolist())      #主标题
    bar_3.set_global_opts(opts.TitleOpts(title="日收益"),toolbox_opts=opts.ToolboxOpts())  #副标题，ToolboxOpts设置工具箱配置项
    bar_3.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项
    for pnl_index in df['net_pnl'].index:
        month_date = pnl_index[:7]
        if not isinstance(df['net_pnl'][pnl_index],np.float64):
            continue
        if month_date == last_month_date:
            month_pnl += df['net_pnl'][pnl_index]
        else:
            #月份减一保存实际月份收益
            month_dict.update({month_date:month_pnl})
            for key,value in list(month_dict.items()):
                if isinstance(key,datetime):
                    continue
                key = datetime.strptime(key,"%Y-%m") - relativedelta(months = 1)
                month_dict.update({key:value})
            #month_dict删除原始的str键值对
            for key,value in list(month_dict.items()):
                if isinstance(key,str):
                    month_dict.pop(key)
            month_pnl = df['net_pnl'][pnl_index]
        last_month_date = month_date
    month_dict.pop(list(month_dict.keys())[0])
    #兼容月度数据不足
    try:
        max_month_pnl = max(month_dict.values())
        min_month_pnl = min(min(month_dict.values()),0)
    except ValueError:
        max_month_pnl = 0
        min_month_pnl = 0
    bar_4 = Bar()
    bar_4.add_xaxis(list(month_dict.keys()))
    bar_4.add_yaxis(f"月盈亏\n\n最大月盈利：{max_month_pnl:,.3f}\n\n最大月亏损：{min_month_pnl:,.3f}",list(month_dict.values()))
    bar_4.set_global_opts(opts.TitleOpts(title="月盈亏"),toolbox_opts=opts.ToolboxOpts())  #设置工具箱配置项
    bar_4.set_series_opts(label_opts=opts.LabelOpts(is_show=False)
    )  #系列配置项

    bar_5 = Bar()
    bar_5.add_xaxis(df["drawdown"].index.tolist())
    bar_5.add_yaxis(f"回撤资金\n\n最大回撤资金：{max_drawdown:,.3f}",df["drawdown"].tolist())      #主标题
    bar_5.set_global_opts(opts.TitleOpts(title="资金"),toolbox_opts=opts.ToolboxOpts())  #副标题，ToolboxOpts设置工具箱配置项
    bar_5.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项

    bar_6 = Bar()
    bar_6.add_xaxis(df["drawdown_percent"].index.tolist())
    bar_6.add_yaxis(f"回撤百分比\n\n最大回撤率：{mad_dd_percent:,.3f}%",df["drawdown_percent"].tolist())      #主标题
    bar_6.set_global_opts(opts.TitleOpts(title="回撤百分比"),toolbox_opts=opts.ToolboxOpts())  #副标题，ToolboxOpts设置工具箱配置项
    bar_6.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项

    hist,bin_edges= np.histogram(df['net_pnl'], bins=50)
    bar_7 = Bar()
    bar_7.add_xaxis(bin_edges[1:].tolist())
    bar_7.add_yaxis("日盈亏分布直方图",hist.tolist())      #主标题
    bar_7.set_global_opts(opts.TitleOpts(title="频数"),toolbox_opts=opts.ToolboxOpts())  #副标题，ToolboxOpts设置工具箱配置项
    bar_7.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项


    bar_8 = Bar()
    bar_8.add_xaxis(df.index.tolist())
    bar_8.add_yaxis(f"日最高手续费：{df['commission'].max()}",df["commission"].tolist())      #主标题
    bar_8.set_global_opts(opts.TitleOpts(title="手续费"),toolbox_opts=opts.ToolboxOpts())  #副标题，ToolboxOpts设置工具箱配置项
    bar_8.set_series_opts(label_opts=opts.LabelOpts(is_show=False))  #系列配置项

    page.add(bar_1)
    page.add(bar_2)
    page.add(bar_3)
    page.add(bar_4)
    page.add(bar_5)
    page.add(bar_6)
    page.add(bar_7)
    page.add(bar_8)
    page.render(ctp_account_path.replace(".csv",".html"))#保存为html
#--------------------------------------------------------------------------------------------
if __name__ == '__main__':
    while True:
        plot_account(total_date=360)#统计最近total_date日数据
        sleep(60)
        break